using Moq;
using NUnit.Framework;
using Tycho.Grammar;
using Tycho.Parser.Peg;

namespace Tycho.UnitTests.Grammar {
    [TestFixture]
    public class DelimitedRuleCompilerSpecification {
        private ITerm Rule;
        private ICaptureCounter MultipleCaptureCounter;
        private Mock<ICaptureCounter> CaptureCounter;
        private Mock<IRuleCompiler> RuleCompiler;
        private CompositeTerm Term;
        private ITerm Delimiter;

        [Test]
        public void ShouldCompileRuleForMultipleCaptures () {
            WithDelimitedCompositeTerm ();
            WithCaptureCounterThatReturnsMultipleCaptureCounter ();

            RuleCompiler = new Mock<IRuleCompiler> ();

            var compiler = new DelimitedRuleCompiler (RuleCompiler.Object);
            var context = new ProductionCompilerContext (null, null, null, null);
            compiler.Compile (Term, context, CaptureCounter.Object);

            AssertThatRuleIsCompiledWithMultipleCaptureCounter (context);
            AssertThatDelimiterIsCompiledWithoutMultipleCaptureCounter (context);
        }

        private void AssertThatDelimiterIsCompiledWithoutMultipleCaptureCounter (ProductionCompilerContext context) {
            RuleCompiler.Verify (r => r.Compile (Delimiter, context, CaptureCounter.Object));
        }

        private void AssertThatRuleIsCompiledWithMultipleCaptureCounter (ProductionCompilerContext context) {
            RuleCompiler.Verify (r => r.Compile (Rule, context, MultipleCaptureCounter));
        }

        private void WithCaptureCounterThatReturnsMultipleCaptureCounter () {
            CaptureCounter = new Mock<ICaptureCounter> ();
            MultipleCaptureCounter = new Mock<ICaptureCounter> ().Object;

            CaptureCounter.Setup(c => c.CreateMultipleCaptureScope ()).Returns (MultipleCaptureCounter);
        }

        private void WithDelimitedCompositeTerm () {
            Term = new CompositeTerm ("name", null);

            Rule = new Mock<ITerm> ().Object;
            Term.Add ("rule", Rule, false);

            Delimiter = new Mock<ITerm> ().Object;
            Term.Add ("delimiter", Delimiter, false);
        }
    }
}